package com.boot.database.mybatis.plus;

import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.boot.database.annotation.TableDefault;
import com.boot.database.service.CurrentUserHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;

import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @description 实体类自动填充处理器
 * @date 2019/11/14 13:57
 **/
@Slf4j
public class ModelAutoFillHandler implements MetaObjectHandler {

    private final AutoFillProperties autoFillProperties;

    private final CurrentUserHandler currentUserHandler;

    public ModelAutoFillHandler(AutoFillProperties autoFillProperties, CurrentUserHandler currentUserHandler) {
        this.autoFillProperties = autoFillProperties;
        this.currentUserHandler = currentUserHandler;
    }

    private static final String CREATE_TIME = "createTime";
    private static final String UPDATE_TIME = "updateTime";
    private static final String CREATE_USER = "createUser";
    private static final String UPDATE_USER = "updateUser";

    @Override
    public void insertFill(MetaObject metaObject) {
        if (autoFillProperties.getEnable()) {
            log.debug("ModelAutoFillHandler::insertFill start");
            if (autoFillProperties.getInsertStrategy().equals(AutoFillProperties.AUTO)) {
                LocalDateTime now = LocalDateTime.now();
                fill(metaObject, CREATE_TIME, now);
                fill(metaObject, UPDATE_TIME, now);
                String username = currentUserHandler.getUsername();
                fill(metaObject, CREATE_USER, username);
                fill(metaObject, UPDATE_USER, username);
            } else {
                LocalDateTime now = LocalDateTime.now();
                this.strictInsertFill(metaObject, CREATE_TIME, LocalDateTime.class, now);
                this.strictInsertFill(metaObject, UPDATE_TIME, LocalDateTime.class, now);
                String username = currentUserHandler.getUsername();
                this.strictInsertFill(metaObject, CREATE_USER, String.class, username);
                this.strictInsertFill(metaObject, UPDATE_USER, String.class, username);
            }
            moduleInsertFill(metaObject, autoFillProperties.getInsertStrategy());
            log.debug("ModelAutoFillHandler::insertFill finish");
        }
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        if (autoFillProperties.getEnable()) {
            log.debug("ModelAutoFillHandler::updateFill start");
            if (autoFillProperties.getUpdateStrategy().equals(AutoFillProperties.AUTO)) {
                LocalDateTime now = LocalDateTime.now();
                fill(metaObject, UPDATE_TIME, now);
                String username = currentUserHandler.getUsername();
                fill(metaObject, UPDATE_USER, username);
            } else {
                LocalDateTime now = LocalDateTime.now();
                this.strictUpdateFill(metaObject, UPDATE_TIME, LocalDateTime.class, now);
                String username = currentUserHandler.getUsername();
                this.strictUpdateFill(metaObject, UPDATE_USER, String.class, username);
            }
            moduleUpdateFill(metaObject, autoFillProperties.getUpdateStrategy());
            log.debug("ModelAutoFillHandler::updateFill finish");
        }
    }

    /**
     * @author 霜寒 <1621856595@qq.com>
     * @description 为个别拥有自定义字段默认值的实体进行填充(Insert条件)
     * @date 2020/1/4 14:21
     **/
    private void moduleInsertFill(MetaObject metaObject, Boolean insertStrategy) {
        Field[] fields = ReflectUtil.getFields(metaObject.getOriginalObject().getClass());
        for (Field field : fields) {
            TableDefault tableDefault = field.getAnnotation(TableDefault.class);
            if (tableDefault != null) {
                Object defaultValue = this.valueOf(tableDefault.value(), tableDefault.type());
                if (insertStrategy.equals(AutoFillProperties.AUTO)) {
                    //                    this.strictInsertFill(metaObject, field.getName(), tableDefault.type(), defaultValue);
                    fill(metaObject, field.getName(), defaultValue);
                } else {
                    if (getFieldValByName(field.getName(), metaObject) == null) {
                        //                        this.setFieldValByName(field.getName(), defaultValue, metaObject);
                        //                        this.strictInsertFill(metaObject, field.getName(), tableDefault.type(), defaultValue);
                        fill(metaObject, field.getName(), defaultValue);
                    }
                }
            }
        }
        log.debug(metaObject.getOriginalObject().toString());
    }

    /**
     * @author 霜寒 <1621856595@qq.com>
     * @description 为个别拥有自定义字段默认值的实体进行填充(Update条件)
     * @date 2020/1/4 14:22
     **/
    private void moduleUpdateFill(MetaObject metaObject, Boolean updateStrategy) {
        Field[] fields = ReflectUtil.getFields(metaObject.getOriginalObject().getClass());
        for (Field field : fields) {
            TableDefault tableDefault = field.getAnnotation(TableDefault.class);
            if (tableDefault != null) {
                Object defaultValue = this.valueOf(tableDefault.value(), tableDefault.type());
                if (updateStrategy.equals(AutoFillProperties.AUTO)) {
                    fill(metaObject, field.getName(), defaultValue);
                } else {
                    if (getFieldValByName(field.getName(), metaObject) == null) {
                        fill(metaObject, field.getName(), defaultValue);
                    }
                }
            }
        }
        log.debug(metaObject.getOriginalObject().toString());
    }

    private void fill(MetaObject metaObject, String fieldName, Object fieldVal) {
        if (metaObject.hasSetter(fieldName)) {
            metaObject.setValue(fieldName, fieldVal);
        }
    }

    /**
     * @author 霜寒 <1621856595@qq.com>
     * @description 将 String 转化为各类型的值
     * @date 2019/12/4 21:06
     **/
    private Object valueOf(String value, Class<?> clazz) {
        if (String.class.equals(clazz)) {
            return value;
        } else if (int.class.equals(clazz)) {
            return Integer.valueOf(value, 10);
        } else if (Integer.class.equals(clazz)) {
            return Integer.valueOf(value, 10);
        } else if (float.class.equals(clazz)) {
            return Float.valueOf(value);
        } else if (Float.class.equals(clazz)) {
            return Float.valueOf(value);
        } else if (double.class.equals(clazz)) {
            return Double.valueOf(value);
        } else if (Double.class.equals(clazz)) {
            return Double.valueOf(value);
        } else if (long.class.equals(clazz)) {
            return Long.valueOf(value, 10);
        } else if (Long.class.equals(clazz)) {
            return Long.valueOf(value, 10);
        } else if (short.class.equals(clazz)) {
            return Short.valueOf(value, 10);
        } else if (Short.class.equals(clazz)) {
            return Short.valueOf(value, 10);
        } else if (byte.class.equals(clazz)) {
            return Byte.valueOf(value, 10);
        } else if (Byte.class.equals(clazz)) {
            return Byte.valueOf(value, 10);
        } else if (boolean.class.equals(clazz)) {
            return Boolean.valueOf(value);
        } else if (Boolean.class.equals(clazz)) {
            return Boolean.valueOf(value);
        } else if (Date.class.equals(clazz)) {
            try {
                if (value.isEmpty()) {
                    return new Date();
                } else {
                    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value);
                }
            } catch (ParseException e) {
                e.printStackTrace();
                return new Date();
            }
        } else if (LocalDateTime.class.equals(clazz)) {
            if (value.isEmpty()) {
                return LocalDateTime.now();
            } else {
                return LocalDateTime.parse(value);
            }
        } else {
            log.warn("未知类型");
            return value;
        }
    }
}
